{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "407829a7-1d18-4263-8caf-d32a69660c00",
   "metadata": {},
   "source": [
    "Chapter 03\n",
    "\n",
    "# Python自定义函数计算矩阵乘法，内积视角\n",
    "《线性代数》 | 鸢尾花书：数学不难"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "48338225-feea-4d87-af1b-bb53e87d335b",
   "metadata": {},
   "source": [
    "这段代码实现的是**矩阵乘法**的几何解释与程序实现，特别强调了**第一视角**（即逐元素构造的视角），并通过 Python 中 `numpy` 模块中的数组操作来实现矩阵乘法的底层过程。下面从**数学角度**对这段代码进行详细解析，包含必要的数学公式，并解释各个步骤在数学中代表的含义。\n",
    "\n",
    "---\n",
    "\n",
    "### **1. 矩阵乘法的数学基础**\n",
    "\n",
    "设有两个矩阵 $A \\in \\mathbb{R}^{m \\times p}$ 和 $B \\in \\mathbb{R}^{p \\times n}$，那么它们的乘积 $C = AB$ 是一个 $m \\times n$ 的矩阵，其中第 $i$ 行第 $j$ 列元素 $C_{ij}$ 的计算方式是：\n",
    "\n",
    "$$\n",
    "C_{ij} = \\sum_{k=1}^{p} A_{ik} B_{kj}\n",
    "$$\n",
    "\n",
    "这就是矩阵乘法的核心定义。\n",
    "\n",
    "等价地，我们也可以将每个元素看作是：\n",
    "$$\n",
    "C_{ij} = \\text{dot}(A_{i,:}, B_{:,j})\n",
    "$$\n",
    "\n",
    "其中：\n",
    "- $A_{i,:}$ 是矩阵 $A$ 的第 $i$ 行（行向量）\n",
    "- $B_{:,j}$ 是矩阵 $B$ 的第 $j$ 列（列向量）\n",
    "\n",
    "它们的点积等价于上面的求和公式。\n",
    "\n",
    "---\n",
    "\n",
    "### **2. 代码功能解释**\n",
    "\n",
    "函数 `matrix_multiplication(A, B)` 实现了上述的数学过程：\n",
    "\n",
    "- 首先，它检查矩阵 $A$ 的列数是否等于矩阵 $B$ 的行数，即 $p_A = p_B$，这是矩阵乘法的基本条件。\n",
    "- 然后，它初始化一个零矩阵 $C \\in \\mathbb{R}^{m \\times n}$，用于存储结果。\n",
    "- 最后，通过两层循环，对每一个位置 $(i,j)$，取出 $A$ 的第 $i$ 行向量 $A_{i,:}$ 和 $B$ 的第 $j$ 列向量 $B_{:,j}$，并用 `np.dot()` 计算内积，即 $C_{ij}$。\n",
    "\n",
    "这种写法提供了“第一视角”的直观性：你可以看作每个元素 $C_{ij}$ 是通过对应的一行一列计算得来的，而不是整块矩阵乘起来的黑箱操作。\n",
    "\n",
    "---\n",
    "\n",
    "### **3. 示例分析**\n",
    "\n",
    "定义矩阵：\n",
    "$$\n",
    "A = \\begin{bmatrix}\n",
    "1 & 2 & 3 \\\\\n",
    "4 & 5 & 6\n",
    "\\end{bmatrix} \\in \\mathbb{R}^{2 \\times 3}\n",
    "$$\n",
    "\n",
    "其转置为：\n",
    "$$\n",
    "B = A^T = \\begin{bmatrix}\n",
    "1 & 4 \\\\\n",
    "2 & 5 \\\\\n",
    "3 & 6\n",
    "\\end{bmatrix} \\in \\mathbb{R}^{3 \\times 2}\n",
    "$$\n",
    "\n",
    "#### **计算 $AB$：**\n",
    "\n",
    "$A \\in \\mathbb{R}^{2 \\times 3}$，$B \\in \\mathbb{R}^{3 \\times 2}$，所以乘积 $C = AB \\in \\mathbb{R}^{2 \\times 2}$，每个元素计算如下：\n",
    "\n",
    "- $C_{11} = 1 \\cdot 1 + 2 \\cdot 2 + 3 \\cdot 3 = 1 + 4 + 9 = 14$\n",
    "- $C_{12} = 1 \\cdot 4 + 2 \\cdot 5 + 3 \\cdot 6 = 4 + 10 + 18 = 32$\n",
    "- $C_{21} = 4 \\cdot 1 + 5 \\cdot 2 + 6 \\cdot 3 = 4 + 10 + 18 = 32$\n",
    "- $C_{22} = 4 \\cdot 4 + 5 \\cdot 5 + 6 \\cdot 6 = 16 + 25 + 36 = 77$\n",
    "\n",
    "结果为：\n",
    "$$\n",
    "AB = \\begin{bmatrix}\n",
    "14 & 32 \\\\\n",
    "32 & 77\n",
    "\\end{bmatrix}\n",
    "$$\n",
    "\n",
    "#### **计算 $BA$：**\n",
    "\n",
    "$B \\in \\mathbb{R}^{3 \\times 2}$，$A \\in \\mathbb{R}^{2 \\times 3}$，所以乘积 $BA \\in \\mathbb{R}^{3 \\times 3}$，每个元素也是按内积逐一计算。\n",
    "\n",
    "比如第一个元素：\n",
    "- $C_{11} = 1 \\cdot 1 + 4 \\cdot 4 = 1 + 16 = 17$\n",
    "\n",
    "以此类推，可以手动验证其他元素，最终结果为：\n",
    "$$\n",
    "BA = \\begin{bmatrix}\n",
    "17 & 22 & 27 \\\\\n",
    "22 & 29 & 36 \\\\\n",
    "27 & 36 & 45\n",
    "\\end{bmatrix}\n",
    "$$\n",
    "\n",
    "---\n",
    "\n",
    "### **4. 总结**\n",
    "\n",
    "此函数从**线性代数中矩阵乘法的定义出发**，以编程方式逐一实现每个元素的计算。通过使用行向量和列向量的内积，本质上是在做**向量空间中的投影**操作，每个结果元素 $C_{ij}$ 都是 $A$ 的第 $i$ 行与 $B$ 的第 $j$ 列在 $p$ 维空间中的“相似度”或“相关性”。\n",
    "\n",
    "该函数虽然不如 NumPy 的 `np.matmul` 或 `@` 运算符高效，但在教学和理解上是极其直观和有价值的。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "27df39fb-584e-478c-8cf5-380731df9bf9",
   "metadata": {},
   "source": [
    "## 初始化"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "9280d771-240f-4618-99fc-4e302aad2fd7",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "48717815-46a7-46b3-b516-fa0cd8b4f67a",
   "metadata": {},
   "source": [
    "## 自定义函数，矩阵乘法第一视角"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "8b609167-1523-4fbd-be50-50a8cffcb220",
   "metadata": {},
   "outputs": [],
   "source": [
    "def matrix_multiplication_inner(A, B):\n",
    "    \n",
    "    # 获取矩阵 A 和 B 的形状\n",
    "    m, p_A = A.shape\n",
    "    p_B, n = B.shape\n",
    "\n",
    "    # 检测矩阵形状是否符合矩阵乘法规则\n",
    "    if p_A != p_B:\n",
    "        raise ValueError('Dimensions do not match')\n",
    "\n",
    "    # 初始化结果矩阵 C，形状 (m, n)，初始值设为 0\n",
    "    C = np.zeros((m, n))\n",
    "\n",
    "    # 计算每个元素\n",
    "    for i in range(m):\n",
    "        for j in range(n):\n",
    "            row_i_A = A[i, :]        # A 的第 i 行（行向量）\n",
    "            col_j_B = B[:, j]        # B 的第 j 列（列向量）\n",
    "            C[i, j] = np.dot(row_i_A, col_j_B)  # 计算内积\n",
    "\n",
    "    return C"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a491fe45-1b5a-465c-a0b2-86d6ea78f71a",
   "metadata": {},
   "source": [
    "## 矩阵乘法"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "0669dd26-522c-4a22-894b-e6f5afb75159",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[1, 2, 3],\n",
       "       [4, 5, 6]])"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A = np.array([[1, 2, 3],\n",
    "              [4, 5, 6]])\n",
    "A"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "c08b9706-5eb1-49f7-87b0-9f2eba3d9897",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[1, 4],\n",
       "       [2, 5],\n",
       "       [3, 6]])"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "B = A.T\n",
    "B"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "50cb3105-4b7d-4388-b52e-8d5e758664f6",
   "metadata": {},
   "source": [
    "## 矩阵乘法"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "10165623-315b-459a-9578-12bc191994e0",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[14., 32.],\n",
       "       [32., 77.]])"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "matrix_multiplication_inner(A, B)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "eed01818-da17-45f9-b9e7-22706d00f163",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[17., 22., 27.],\n",
       "       [22., 29., 36.],\n",
       "       [27., 36., 45.]])"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "matrix_multiplication_inner(B, A)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b761edf5-05fd-46a0-8c46-1c849c1df004",
   "metadata": {},
   "source": [
    "作者\t**生姜DrGinger**  \n",
    "脚本\t**生姜DrGinger**  \n",
    "视频\t**崔崔CuiCui**  \n",
    "开源资源\t[**GitHub**](https://github.com/Visualize-ML)  \n",
    "平台\t[**油管**](https://www.youtube.com/@DrGinger_Jiang)\t\t\n",
    "\t\t[**iris小课堂**](https://space.bilibili.com/3546865719052873)\t\t\n",
    "\t\t[**生姜DrGinger**](https://space.bilibili.com/513194466)  "
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python [conda env:base] *",
   "language": "python",
   "name": "conda-base-py"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
